#include <stdio.h>
#include <malloc.h>
#include <assert.h>
#include "mem.h"

static mem_state_t global_mem;
static mem_config_t global_memmap;



static unsigned int mem_read_word(unsigned int addr);
static unsigned int mem_read_halfword(unsigned int addr);
static unsigned int mem_read_byte(unsigned int addr);

static unsigned int mem_write_word(unsigned int addr, unsigned int value);
static unsigned int mem_write_halfword(unsigned int addr, unsigned int value);
static unsigned int mem_write_byte(unsigned int addr, unsigned int value);

mem_config_t* get_global_memmap(void){
	return &global_memmap;
}

mem_state_t* get_global_mem(void){
	return &global_mem;
}




unsigned int mem_init(void){

	mem_config_t *mmap = NULL;
	mem_state_t* mem = NULL;
	int i;

	mmap = get_global_memmap();
	mem = get_global_mem();

	for(i = 0; i < mmap->total_bank_num; i++){
		if(mmap->mem_banks[i].type == MEM_TYPE){
			mem->rom_size[i] = mmap->mem_banks[i].size;
			mem->rom[i] = malloc(mmap->mem_banks[i].size);
		}
		else if(mmap->mem_banks[i].type == IO_TYPE){
			mem->rom_size[i] = mmap->mem_banks[i].size;
			mem->rom[i] = NULL;
		}
	}	


}

mem_bank_t* bank_ptr(unsigned int addr){


	mem_config_t *local_mmap = NULL;
	int i;

	local_mmap = get_global_memmap();


	for(i = 0; i < local_mmap->total_bank_num; i++){
		if((addr >= local_mmap->mem_banks[i].start_addr) && (addr < (local_mmap->mem_banks[i].start_addr + local_mmap->mem_banks[i].size))){
		return &(local_mmap->mem_banks[i]);
	}
		else{
		
		}
	}

	return NULL;
}

int mem_read(unsigned int addr, unsigned int size, unsigned int *value){

	switch(size){

		case 8:
			*value = mem_read_byte(addr);
			break;
		case 16:
			*value = mem_read_halfword(addr);
			break;
		case 32:
			*value = mem_read_word(addr);
			break;

		default:
			break;
			


	}

	return SUCCESS;

}

int mem_write(unsigned int addr, unsigned int size, unsigned value){


	switch(size){
		
		case 8:
			mem_write_byte(addr,value);
			break;
		case 16:
			mem_write_halfword(addr,value);
			break;
		case 32:
			mem_write_word(addr,value);
			break;

		default:
			break;

	}

	return SUCCESS;
}





static unsigned int mem_read_word(unsigned int addr){
	
	unsigned int data ;
	
	mem_config_t *local_mmap = NULL;
	mem_state_t* local_mem = NULL;
	mem_bank_t* local_bank = NULL;

	local_mmap = get_global_memmap();
	local_mem = get_global_mem();

	local_bank = bank_ptr(addr);

	assert((local_bank - local_mmap->mem_banks) >= 0);

	data = local_mem->rom[local_bank - local_mmap->mem_banks][(addr - local_mmap->mem_banks[local_bank - local_mmap->mem_banks].start_addr) >> 2];

	return data;

}

static unsigned int mem_read_halfword(unsigned int addr){

	unsigned int data ;
	mem_config_t *local_mmap = NULL;
	mem_state_t* local_mem = NULL;
	mem_bank_t* local_bank = NULL;

	local_mmap = get_global_memmap();
	local_mem = get_global_mem();

	local_bank = bank_ptr(addr);

	assert((local_bank - local_mmap->mem_banks) >= 0);

	data = local_mem->rom[local_bank - local_mmap->mem_banks][(addr - local_mmap->mem_banks[local_bank - local_mmap->mem_banks].start_addr) >> 2];

	return data;



}

static unsigned int mem_read_byte(unsigned int addr){

	unsigned int data ;
	mem_config_t *local_mmap = NULL;
	mem_state_t* local_mem = NULL;
	mem_bank_t* local_bank = NULL;

	local_mmap = get_global_memmap();
	local_mem = get_global_mem();

	local_bank = bank_ptr(addr);

	assert((local_bank - local_mmap->mem_banks) >= 0);

	data = local_mem->rom[local_bank - local_mmap->mem_banks][(addr - local_mmap->mem_banks[local_bank - local_mmap->mem_banks].start_addr) >> 2];

	return data;



}

static unsigned int mem_write_word(unsigned int addr, unsigned int value){

	unsigned int *data ;
	mem_config_t *local_mmap = NULL;
	mem_state_t* local_mem = NULL;
	mem_bank_t* local_bank = NULL;

	local_mmap = get_global_memmap();
	local_mem = get_global_mem();

	local_bank = bank_ptr(addr);

	assert((local_bank - local_mmap->mem_banks) >= 0);
	data = &(local_mem->rom[local_bank - local_mmap->mem_banks][(addr - local_mmap->mem_banks[local_bank - local_mmap->mem_banks].start_addr) >> 2]);

	*data = value;

	return SUCCESS;

}

static unsigned int mem_write_halfword(unsigned int addr, unsigned int value){


	unsigned int *data;
	unsigned int addr_offset;
	mem_config_t *local_mmap = NULL;
	mem_state_t* local_mem = NULL;
	mem_bank_t* local_bank = NULL;

	local_mmap = get_global_memmap();
	local_mem = get_global_mem();

	local_bank = bank_ptr(addr);
	addr_offset = addr & 2;	

	assert((local_bank - local_mmap->mem_banks) >= 0);
	data = &(local_mem->rom[local_bank - local_mmap->mem_banks][(addr - local_mmap->mem_banks[local_bank - local_mmap->mem_banks].start_addr) >> 2]);

	addr_offset ? (*data = ((*data & 0xffff0000)|(value & 0xffff))) : (*data = ((*data & 0x0000ffff) | (value & 0xffff) << 16));

	return SUCCESS;

}

static unsigned int mem_write_byte(unsigned int addr, unsigned int  value){


	unsigned int *data;
	unsigned int addr_offset;
	mem_config_t *local_mmap = NULL;
	mem_state_t* local_mem = NULL;
	mem_bank_t* local_bank = NULL;

	local_mmap = get_global_memmap();
	local_mem = get_global_mem();

	local_bank = bank_ptr(addr);
	addr_offset = addr & 3;

	assert((local_bank - local_mmap->mem_banks) >= 0);
	data = &(local_mem->rom[local_bank - local_mmap->mem_banks][(addr - local_mmap->mem_banks[local_bank - local_mmap->mem_banks].start_addr) >> 2]);

	*data = ((*data & ~(0xff << (4*addr_offset))) | (value & 0xff) << (4*addr_offset));
	
	return SUCCESS;

}


